//object.defineProperty无法兼容 ie8及以下
//data中的数据使用defineProperty重新定义
import { isObject, def } from "../util/index";
import { arrayMethods } from "./array";
import Dep from "./dep";

class Observer {
    constructor(value) {
        this.dep = new Dep();
        def(value, "__ob__", this); //给每个监控的对象增加__ob__属性
        if (Array.isArray(value)) {
            //数据是数组不观测索引，因为会导致性能问题
            //重写push unshift shift方法
            value.__proto__ = arrayMethods;
            this.observerArray(value); //虽然递归了，但没有依赖收集
        } else {
            this.walk(value);
        }
    }
    observerArray(value) {
        for (let i = 0; i < value.length; i++) {
            observe(value[i]);
        }
    }
    walk(data) {
        let keys = Object.keys(data); // [name,age,address]
        keys.forEach((key) => {
            defineReactive(data, key, data[key]);
        });
    }
}

function defineReactive(data, key, value) {
    let dep = new Dep();

    let childObj = observe(value); //返回observer的实例
    Object.defineProperty(data, key, {
        configurable: true,
        enumerable: true,
        get() {
            console.log("取值");
            if (Dep.target) {
                dep.depend();
                if (childObj) {
                    //主要是数组的依赖收集
                    childObj.dep.depend();
                    //如果数组中还有数组
                    if (Array.isArray(value)) {
                        dependArray(value);
                    }
                }
            }
            return value;
        },
        set(newValue) {
            if (newValue === value) return;
            observe(newValue); //继续劫持， 因为用户设置的值可能是个对象
            value = newValue;
            dep.notify();
        },
    });
}
function dependArray(value) {
    for (let i = 0; i < value.length; i++) {
        let current = value[i];
        //数组中的数组的依赖收集
        current.__ob__ && current.__ob__.dep.depend();
        if (Array.isArray(current)) {
            dependArray(current);
        }
    }
}

export function observe(data) {
    let isObj = isObject(data);
    if (!isObj) return;
    let ob;
    if (hasOwn(data, "__ob__") && data.__ob__ instanceof Observer) {
        //如果value已经是响应式数据，不需要再次创建Observer实例
        ob = data.__ob__;
    } else {
        ob = new Observer(data);
    }
    return ob;
}

//vm.$set实现原理
export function set(target, key, val) {
    //1 target是数组且key为有效索引
    if (Array.isArray(target) && isValidArrayIndex(key)) {
        target.length = Math.max(target.length, key);
        target.splice(key, 1, val);
        return val;
    }
    //2 key已经存在于target中
    if (key in target && !(key in Object.prototype)) {
        target[key] = val;
        return val;
    }
    //3 处理新增的属性
    const ob = target.__ob__;
    if (target._isVue || (ob && ob.vmCount)) {
        process.env.NODE_ENV !== "production" &&
            warn(
                "Avoid adding reactive properties to a Vue instance or its root $data " +
                    "at runtime - declare it upfront in the data option."
            );
        return val;
    }
    if (!ob) {//非响应式情况
        target[key] = val;
        return val;
    }
    defineReactive(ob.value, key, val);
    ob.dep.notify();
    return val;
}
